// copyright jyz.2024


#include "AuraPlayerController.h"

#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "AuraGameplayTags.h"
#include "InputAction.h"
#include "InputActionValue.h"
#include "EnhancedInputSubsystems.h"
#include "NavigationPath.h"
#include "NavigationSystem.h"
#include "AbilitySystem/AuraAbilitySystemComponent.h"
#include "AuraRPGDemo/Interaction/EnemyInterface.h"
#include "Components/SplineComponent.h"
#include "Input/AuraEnhancedInputComponent.h"
#include "GameFramework/Character.h"
#include "UI/Widget/DamageTextComponent.h"

AAuraPlayerController::AAuraPlayerController()
{
	//启用网络复制
	//只有标记为UPROPERTY(Replicated)的属性才会被复制。
	bReplicates = true;
	SplineComponent = CreateDefaultSubobject<USplineComponent>("SplineComponent");
	SplineComponent->AttachToComponent(RootComponent,FAttachmentTransformRules::KeepRelativeTransform);
}

void AAuraPlayerController::ShowDamageNumber_Implementation(float DamageAmount,ACharacter* TargetCharacter)
{
	if(IsValid(TargetCharacter)&&DamageTextComponentClass)
	{
		FVector Origin, BoxExtent;
		TargetCharacter->GetActorBounds(true,Origin, BoxExtent);
		UDamageTextComponent* DamageTextComp = NewObject<UDamageTextComponent>(TargetCharacter,DamageTextComponentClass);
		DamageTextComp->RegisterComponent();
		DamageTextComp->AttachToComponent(TargetCharacter->GetRootComponent(),FAttachmentTransformRules::KeepRelativeTransform);
		DamageTextComp->SetWorldLocation(Origin+FVector(0,0,BoxExtent.Z));
		DamageTextComp->SetDamageText(DamageAmount);
		DamageTextComp->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
	}
}

void AAuraPlayerController::BeginPlay()
{
	Super::BeginPlay();
	check(IMC_AuraContext);
	UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer());
	if(Subsystem)
	{
		Subsystem->AddMappingContext(IMC_AuraContext,0);
	}
	bShowMouseCursor =true;
	DefaultMouseCursor =EMouseCursor::Default;

	FInputModeGameAndUI InputModeData;
	//这里选择的是 EMouseLockMode::DoNotLock，代表鼠标不会被锁定在视口中，也就是鼠标可以自由地从视口中移出来。
	InputModeData.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock);
	//设置在捕获过程中是否隐藏光标。参数 false 表示光标在捕获过程中不会被隐藏，也就是说在任何时候都可以看到鼠标光标。
	InputModeData.SetHideCursorDuringCapture(false);
	SetInputMode(InputModeData);
}

void AAuraPlayerController::PlayerTick(float DeltaTime)
{
	Super::PlayerTick(DeltaTime);
	CursorTrace();
	AutoRun();
}

void AAuraPlayerController::SetupInputComponent()
{
	Super::SetupInputComponent();
	UAuraEnhancedInputComponent*AuraInputComponent = CastChecked<UAuraEnhancedInputComponent>(InputComponent);
	AuraInputComponent->BindAction(IA_MoveAction,ETriggerEvent::Triggered,this,&AAuraPlayerController::OnMove);
	AuraInputComponent->BindAction(IA_ShiftAction,ETriggerEvent::Started,this,&AAuraPlayerController::OnShiftPressed);
	AuraInputComponent->BindAction(IA_ShiftAction,ETriggerEvent::Completed,this,&AAuraPlayerController::OnShiftReleased);
	AuraInputComponent->BindAbilityActions(
		InputConfig,
		this,
		&AAuraPlayerController::OnInputTagPressed,
		&AAuraPlayerController::OnInputTagReleased,
		&AAuraPlayerController::OnInputTagHeld);
}

void AAuraPlayerController::OnMove(const FInputActionValue& InValue)
{
	bAutoRunning = false;
	const FVector2D InputAxisVector2D = InValue.Get<FVector2D>();
	const FRotator YawRot(0.f,GetControlRotation().Yaw,0);

	const FVector ForwardDirection = FRotationMatrix(YawRot).GetUnitAxis(EAxis::X);
	const FVector RightDirection = FRotationMatrix(YawRot).GetUnitAxis(EAxis::Y);
	if(APawn*ControlPawn = GetPawn())
	{
		ControlPawn->AddMovementInput(ForwardDirection,InputAxisVector2D.Y);
		ControlPawn->AddMovementInput(RightDirection,InputAxisVector2D.X);
	}
}

void AAuraPlayerController::OnShiftPressed(const FInputActionValue& InValue)
{
	bShiftKeyDown = true;
}

void AAuraPlayerController::OnShiftReleased(const FInputActionValue& InValue)
{
	bShiftKeyDown = false;
}

void AAuraPlayerController::OnInputTagPressed(FGameplayTag InputTag)
{
	if(InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_RMB))
	{
		bTargeting = ThisActor!=nullptr ? true : false;
		bAutoRunning = false;
	}
}

void AAuraPlayerController::OnInputTagReleased(FGameplayTag InputTag)
{
	if(GetASC())
    {
    	GetASC()->AbilityInputTagReleased(InputTag);
    }
	if(!bTargeting&&!bShiftKeyDown)
	{
		if(InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_RMB))
		{
			//移动到鼠标点击位置，短按的情况下
			MoveToCursorFromShortPressed();
		}
	
	}
	
}

void AAuraPlayerController::OnInputTagHeld(FGameplayTag InputTag)
{
	if(bTargeting||bShiftKeyDown)
	{
		if(GetASC())
		{
			GetASC()->AbilityInputTagHeld(InputTag);
		}
		return;
	}
	if(InputTag.MatchesTagExact(FAuraGameplayTags::Get().InputTag_RMB))
	{
		// OnClick On Move
		FollowTime += GetWorld()->GetDeltaSeconds();
		//在光标下获取碰撞结果
		if(CursorHit.bBlockingHit)
		{
			// ImpactPoint：这是击中表面的精确点。即使发生了交错或滑动，也总是表示碰撞发生的确切位置。
			// Location：这是将碰撞位置沿着投射向量拉回到碰撞体积的表面的点。
			// 所以说，这两者之间的区别在于它们在碰撞发生后对位置的表述。
			// ImpactPoint总是实际碰撞发生的地点，而Location可能基于碰撞体积进行了调整。
			CacheDestination = CursorHit.ImpactPoint;
		}
		if(APawn* ControlledPawn = GetPawn())
		{
			const FVector WorldDirVector = (CacheDestination - ControlledPawn->GetActorLocation()).GetSafeNormal();
			ControlledPawn->AddMovementInput(WorldDirVector,1,false);
		}
	}
	
}

void AAuraPlayerController::CursorTrace()
{
	GetHitResultUnderCursor(ECC_Visibility,false,CursorHit);
	if(!CursorHit.bBlockingHit)
		return;

	LastActor = ThisActor;
	ThisActor = Cast<IEnemyInterface>(CursorHit.GetActor());

	if(LastActor != ThisActor)
	{
		if(LastActor)
		{
			LastActor->SetHighlightActor(false);
		}
		if(ThisActor)
		{
			ThisActor->SetHighlightActor(true);
		}
	}
}

void AAuraPlayerController::MoveToCursorFromShortPressed()
{
	APawn* ControlledPawn = GetPawn();
	if(FollowTime < ShortPressThreshold&&ControlledPawn)
	{
		UNavigationPath* NavigationPath =  UNavigationSystemV1::FindPathToLocationSynchronously(this,
			ControlledPawn->GetActorLocation(),
			CacheDestination);
		SplineComponent->ClearSplinePoints();
		for(const FVector& Point:NavigationPath->PathPoints)
		{
			SplineComponent->AddSplinePoint(Point,ESplineCoordinateSpace::World);
			DrawDebugSphere(GetWorld(),Point,8.f,8,FColor::Cyan,false,5.f);
		}
		if(NavigationPath->PathPoints.IsValidIndex(0))
		{
			CacheDestination = NavigationPath->PathPoints[NavigationPath->PathPoints.Num() - 1 ];
			bAutoRunning = true;
		}
	}
	FollowTime = 0.f;
	bTargeting = false;
}

void AAuraPlayerController::AutoRun()
{
	if(bAutoRunning)
	{
		if(APawn* ControlledPawn = GetPawn())
		{
			const FVector LocationOnSpline = SplineComponent->FindLocationClosestToWorldLocation(ControlledPawn->GetActorLocation(),ESplineCoordinateSpace::World);
			const FVector Direction = SplineComponent->FindDirectionClosestToWorldLocation(LocationOnSpline,ESplineCoordinateSpace::World);
			ControlledPawn->AddMovementInput(Direction,1,false);

			const float DistanceToDestination = (LocationOnSpline - CacheDestination).Length();
			if(DistanceToDestination < AutoRunAcceptanceRadius)
			{
				bAutoRunning = false;
			}
		}
	}
	
}

UAuraAbilitySystemComponent* AAuraPlayerController::GetASC()
{
	if(AuraASC==nullptr)
	{
		AuraASC = Cast<UAuraAbilitySystemComponent>(
			UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(GetPawn()));
	}
	return AuraASC;
}
